#include <string>
#include <memory>
#include <vector>
#include <node_api.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/select.h>
#include "log/serialport_log_wrapper.h"
#include "i_serialport_client.h"
#include "x_napi_tool.h"
#include "serial_async_callback.h"
#include "serial_opt.h"
#include "napi/native_api.h"
#include "napi/native_common.h"
#include <hilog/log.h>

#define NUMBER_JS_2_C(napi_v, type, dest)   dest = pxt->SwapJs2C##type(napi_v)
#define NUMBER_C_2_JS(pxt, type, n) pxt->SwapC2Js##type(n)

#define BOOLEAN_JS_2_C(napi_v, type, dest)   dest = pxt->SwapJs2C##type(napi_v)
#define BOOLEAN_C_2_JS(pxt, type, n) pxt->SwapC2Js##type(n)

void SetOptions_execute(XNapiTool *pxt, void *data)
{
    SetOptions_value_struct *vio = (SetOptions_value_struct *)data;
    vio->out = SetOptions(vio->in0, vio->in1, vio->in2, vio->in3, vio->in4);
    return;
}

void SetOptions_complete(XNapiTool *pxt, void *data)
{
    int32_t ret = -1;
    SetOptions_value_struct *vio = (SetOptions_value_struct *)data;
    napi_value result = nullptr;
    result = NUMBER_C_2_JS(pxt, Int32, vio->out);
    {
        napi_value args[XNapiTool::ARGV_CNT] = {result, nullptr};
        if(vio->out >= 0)
            ret = 0;
        pxt->FinishAsync(ret, args);
    }
    delete vio;
}

napi_value SetOptions_middle(napi_env env, napi_callback_info info)
{
    XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
    if (pxt->IsFailed()) {
        napi_value err = pxt->GetError();
        delete pxt;
        return err;
    }

    struct SetOptions_value_struct *vio = new SetOptions_value_struct();
    
    vio->in0 = pxt->SwapJs2CInt32(pxt->GetArgv(0));
    NUMBER_JS_2_C(pxt->GetArgv(1),Int32,vio->in1);
    NUMBER_JS_2_C(pxt->GetArgv(2),Int32,vio->in2);
    NUMBER_JS_2_C(pxt->GetArgv(3),Int32,vio->in3);
    NUMBER_JS_2_C(pxt->GetArgv(4),Int32,vio->in4);
    
    napi_value result = pxt->StartAsync(SetOptions_execute, vio, SetOptions_complete,
        pxt->GetArgc() == 6 ? pxt->GetArgv(5) : nullptr);

    if (pxt->IsFailed()) {
        result = pxt->GetError();
    }
    return result;
}

void OpenSerial_execute(XNapiTool *pxt, void *data)
{
    OpenSerial_value_struct *vio = (OpenSerial_value_struct *)data;
    vio->out = OpenSerial(vio->in0.c_str());
    return;
}

void OpenSerial_complete(XNapiTool *pxt, void *data)
{
    int32_t ret = -1;
    OpenSerial_value_struct *vio = (OpenSerial_value_struct *)data;
    napi_value result = nullptr;
    result = NUMBER_C_2_JS(pxt, Int32, vio->out);
    {
        napi_value args[XNapiTool::ARGV_CNT] = {result, nullptr};
        if(vio->out > 0)
            ret = 0;
        pxt->FinishAsync(ret, args);
    }
    delete vio;
}
napi_value OpenSerial_middle(napi_env env, napi_callback_info info)
{
    XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
    if (pxt->IsFailed()) {
        napi_value err = pxt->GetError();
        delete pxt;
        return err;
    }

    struct OpenSerial_value_struct *vio = new OpenSerial_value_struct();
    pxt->SwapJs2CUtf8(pxt->GetArgv(0), vio->in0);
    napi_value result = pxt->StartAsync(OpenSerial_execute, vio, OpenSerial_complete,
        pxt->GetArgc() == 2 ? pxt->GetArgv(1) : nullptr);
    if (pxt->IsFailed()) {
        result = pxt->GetError();
    }
    return result;
}

void CloseSerial_execute(XNapiTool *pxt, void *data)
{
    CloseSerial_value_struct *vio = (CloseSerial_value_struct *)data;
    vio->out = CloseSerial(vio->in0);
}

void CloseSerial_complete(XNapiTool *pxt, void *data)
{
    CloseSerial_value_struct *vio = (CloseSerial_value_struct *)data;
    napi_value result = nullptr;
    result = NUMBER_C_2_JS(pxt, Int32, vio->out);
    {
        napi_value args[XNapiTool::ARGV_CNT] = {result, nullptr};
        pxt->FinishAsync(vio->out, args);
    }

    delete vio;
}

napi_value CloseSerial_middle(napi_env env, napi_callback_info info)
{
    XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
    if (pxt->IsFailed()) {
        napi_value err = pxt->GetError();
        delete pxt;
        return err;
    }
    struct CloseSerial_value_struct *vio = new CloseSerial_value_struct();
    vio->in0 = pxt->SwapJs2CInt32(pxt->GetArgv(0));
    napi_value result = pxt->StartAsync(CloseSerial_execute, vio, CloseSerial_complete,
        pxt->GetArgc() == 2 ? pxt->GetArgv(1) : nullptr);

    if (pxt->IsFailed()) {
        result = pxt->GetError();
    }
    return result;
}

void SendData_execute(XNapiTool *pxt, void *data)
{
    SendData_value_struct *vio = (SendData_value_struct *)data;
	vio->out = SendData(vio->in0, vio->in1.data(), vio->in1.size());
}

void SendData_complete(XNapiTool *pxt, void *data)
{
    int32_t ret = -1;
	
    SendData_value_struct *vio = (SendData_value_struct *)data;
    napi_value result = nullptr;
    result = NUMBER_C_2_JS(pxt, Int32, vio->out);
    {
        if(vio->out > 0)
            ret = 0;
        napi_value args[XNapiTool::ARGV_CNT] = {result, nullptr};
        pxt->FinishAsync(ret, args);
    }

    delete vio;
}

napi_value SendData_middle(napi_env env, napi_callback_info info)
{
    XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
    if (pxt->IsFailed()) {
        napi_value err = pxt->GetError();
        delete pxt;
        return err;
    }

    struct SendData_value_struct *vio = new SendData_value_struct();
    vio->in0 = pxt->SwapJs2CInt32(pxt->GetArgv(0));
    pxt->SwapJs2CUint8Array(pxt->GetArgv(1), vio->in1);

    napi_value result = pxt->StartAsync(SendData_execute, vio, SendData_complete,
        pxt->GetArgc() == 3 ? pxt->GetArgv(2) : nullptr);

    if (pxt->IsFailed()) {
        result = pxt->GetError();
    }
    return result;
}

void RecvData_execute(XNapiTool *pxt, void *data)
{
    int32_t i;
    RecvData_value_struct *vio = (RecvData_value_struct *)data;
    std::vector<uint8_t> readata(vio->len);
    vio->out = RecvDat(vio->in0, readata.data(), readata.size(), vio->timeout);
    for(i=0; i<vio->out; i++)
        vio->outData.push_back(readata[i]);
}

void RecvData_complete(XNapiTool *pxt, void *data)
{
    int32_t ret = -1;
		
    RecvData_value_struct *vio = (RecvData_value_struct *)data;
    napi_value result = NUMBER_C_2_JS(pxt, Int32, vio->out);
	napi_value outData = pxt->SwapC2JsUint8Array(vio->outData.data(), vio->outData.size());	
    {
        if(vio->out > 0)
            ret = 0;
        napi_value args[XNapiTool::ARGV_CNT] = {result, outData};
        pxt->FinishAsync(ret, args);
    }

    delete vio;
}

napi_value RecvData_middle(napi_env env, napi_callback_info info)
{
    XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
    if (pxt->IsFailed()) {
        napi_value err = pxt->GetError();
        delete pxt;
        return err;
    }
    struct RecvData_value_struct *vio = new RecvData_value_struct();
    vio->in0 = pxt->SwapJs2CInt32(pxt->GetArgv(0));
    vio->len = pxt->SwapJs2CInt32(pxt->GetArgv(1));
    vio->timeout = pxt->SwapJs2CInt32(pxt->GetArgv(2));
	
    napi_value result = pxt->StartAsync(RecvData_execute, vio, RecvData_complete,
        pxt->GetArgc() == 4 ? pxt->GetArgv(3) : nullptr);

    if (pxt->IsFailed()) {
        result = pxt->GetError();
    }
    return result;
}

EXTERN_C_START
static napi_value init(napi_env env, napi_value exports)
{
    std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, exports);
    pxt->DefineFunction("setOptions", SetOptions_middle);
    pxt->DefineFunction("openSerial", OpenSerial_middle);
    pxt->DefineFunction("closeSerial", CloseSerial_middle);
    pxt->DefineFunction("sendData", SendData_middle);
    pxt->DefineFunction("recvData", RecvData_middle);
    return exports;
}
EXTERN_C_END
static napi_module g_serialHelper_Module = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = init,
    .nm_modname = "serialhelper",
    .nm_priv = ((void *)0),
    .reserved = {(void *)0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&g_serialHelper_Module);
}
